import { Random } from 'mockjs'
import { HttpResponse, http } from 'msw'

import type { DefaultBodyType, HttpHandler, StrictRequest, StrictResponse } from 'msw'
import { parseJWT } from '@/utils/jwt'
import { Role } from '@/constants/role'
import { GlobalCustomCode, globalMessage } from '@/apis/message'
import { UserCustomCode, userMessage } from '@/apis/user/message'

import type { LoginPayload, LoginSuccessData } from '@/apis/user/types'

/** 鉴权中间件 */
export function auth<T extends DefaultBodyType>(
  request: StrictRequest<T>
): StrictResponse<DefaultBodyType> | void {
  const bearerToken = request.headers.get('Authorization')

  if (bearerToken === null || !bearerToken.startsWith('Bearer ')) {
    return HttpResponse.json(
      {
        code: GlobalCustomCode.BadRequest,
        err: globalMessage['zh-CN'][GlobalCustomCode.BadRequest]
      },
      { status: 400 }
    )
  }

  if (getAuthInfo(request).exp < Date.now() / 1000) {
    return HttpResponse.json(
      {
        code: GlobalCustomCode.Forbidden,
        err: globalMessage['zh-CN'][GlobalCustomCode.Forbidden]
      },
      { status: 401 }
    )
  }
}

export function getAuthInfo(request: StrictRequest<DefaultBodyType>) {
  const bearerToken = request.headers.get('Authorization')
  return parseJWT<{ exp: number; uid: string; role: Role }>(bearerToken!.replace('Bearer ', ''))
}

export function createUserHandlers(prefix: string): HttpHandler[] {
  const getPath = (path = '') => `${prefix}/users${path}`

  return [
    http.post<never, LoginPayload>(getPath('/login'), async ({ request }) => {
      const body = await request.json()
      const role = +body.username.slice(-2)
      const isRole = !Number.isNaN(role) && role > 0 && role <= Role.All

      if (body.username.length === 8 && isRole && body.password === body.username) {
        return HttpResponse.json<LoginSuccessData>(
          {
            code: UserCustomCode.LoginSuccess,
            data: {
              accessToken: mockJWT(10 * 60, role, body.username),
              refreshToken: mockJWT(7 * 24 * 60 * 60, role, body.username)
            }
          },
          { status: 200 }
        )
      }

      return HttpResponse.json(
        {
          code: UserCustomCode.UserNotMatch,
          err: userMessage['zh-CN'][UserCustomCode.UserNotMatch]
        },
        { status: 401 }
      )
    }),
    http.post<never, { refreshToken: string }>(getPath('/refresh'), async ({ request }) => {
      const { refreshToken } = await request.json()
      const { exp, uid, role } = parseJWT<{ exp: number; uid: string; role: Role }>(refreshToken)
      if (exp * 1000 < Date.now()) {
        return HttpResponse.json(
          {
            code: GlobalCustomCode.UnAuthorized,
            err: 'refresh token expired'
          },
          {
            status: 401
          }
        )
      }

      return HttpResponse.json({
        code: UserCustomCode.SuccessRefresh,
        data: {
          accessToken: mockJWT(10 * 60, role, uid)
        }
      })
    }),
    http.get<never, never>(getPath('/profile'), async ({ request }) => {
      const authError = auth(request)
      if (authError !== undefined) return authError
      const { uid, role } = getAuthInfo(request)

      return HttpResponse.json(
        {
          code: UserCustomCode.GetProfileSuccess,
          data: { name: Random.cname(), username: uid, role }
        },
        { status: 200 }
      )
    })
  ]
}

function mockJWT(expire: number, role: Role, uid: string) {
  const iat = ~~(Date.now() / 1000)

  return `${Random.string(10)}.${btoa(
    JSON.stringify({ iat, exp: iat + expire, uid, role })
  )}.${Random.string(10)}`
}
